#include "softwareiic.h"
#include "soc/soc_misc.h"
#include "soc/soc_gpio.h"


#define IIC_WAIT_ACK_TIMEOUT_CNT  250

#define IIC_DELAY()  for(int ii = 0; ii < 50; ii++); //SOC_Misc::DelayUs(m_clkDelay) //
#define IIC_SCL(v)   SOC_Gpio::Write(m_SCLPortPin, v)
#define IIC_SDA(v)   SOC_Gpio::Write(m_SDAPortPin, v)
#define IIC_READ_SDA()  SOC_Gpio::Read(m_SDAPortPin)

#define WAIT_ACK_AND_RET_IF_ERROR() do{\
    if(WaitAckSignal() == false) \
    {\
        StopSignal();\
        return false;\
    }\
}while(0)

SoftWareIIC::SoftWareIIC()
{
}

SoftWareIIC::SoftWareIIC(const GpioPortPin &portSCL, const GpioPortPin &portSDA)
{
    Config(portSCL, portSDA);
}

SoftWareIIC::~SoftWareIIC()
{
}

void SoftWareIIC::Config(const GpioPortPin &sclPortPin, const GpioPortPin &sdaPortPin)
{
    m_SCLPortPin = sclPortPin;
    m_SDAPortPin = sdaPortPin;
}

void SoftWareIIC::SetClkDelay(int us)
{
    m_clkDelay = us;
}

bool SoftWareIIC::Write(uint8_t addr, uint8_t reg, uint8_t *buffer, int len)
{
    StartSignal();
    SendByte((addr << 1) | 0);
    WAIT_ACK_AND_RET_IF_ERROR();

    SendByte(reg);
    WAIT_ACK_AND_RET_IF_ERROR();

    for(int i = 0; i < len; i++)
    {
        SendByte(buffer[i]);
        WAIT_ACK_AND_RET_IF_ERROR();
    }

    StopSignal();
    return true;
}

bool SoftWareIIC::Read(uint8_t addr, uint8_t reg, uint8_t *buffer, int len)
{
    StartSignal();
    SendByte((addr<<1) | 0);
    WAIT_ACK_AND_RET_IF_ERROR();

    SendByte(reg);
    WAIT_ACK_AND_RET_IF_ERROR();

    StartSignal();
    SendByte((addr<<1) | 1);
    WAIT_ACK_AND_RET_IF_ERROR();

    while(len > 0)
    {
        *buffer = ReceiveByte((len > 1) ? true : false);
        len--;
        buffer++;
    }
    
    StopSignal();
    return true;
}

bool SoftWareIIC::WriteByte(uint8_t addr, uint8_t reg, uint8_t val)
{
    return Write(addr, reg, (uint8_t*)&val, 1);
}

bool SoftWareIIC::ReadByte(uint8_t addr, uint8_t reg, uint8_t &val)
{
    return Read(addr, reg, (uint8_t*)&val, 1);
}

int SoftWareIIC::read()
{
    return 0;
}

size_t SoftWareIIC::write(uint8_t c)
{
    return 1;
}

void SoftWareIIC::StartSignal()
{
    IIC_SDA(1);
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
}

void SoftWareIIC::StopSignal()
{
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SDA(1);
    IIC_DELAY();
}

bool SoftWareIIC::WaitAckSignal()
{
    int waittime = 0;
    bool ok = true;
    
    IIC_SDA(1);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    
    while (IIC_READ_SDA())
    {
        waittime++;
        
        if (waittime > IIC_WAIT_ACK_TIMEOUT_CNT)
        {
            StopSignal();
            ok = false;
            break;
        }
    }
    
    IIC_SCL(0);
    IIC_DELAY();
    
    return ok;
}

void SoftWareIIC::AckSignal()
{
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
    IIC_SDA(1);
    IIC_DELAY();
}

void SoftWareIIC::NAckSignal()
{
    IIC_SDA(1);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
}

bool SoftWareIIC::SendByte(uint8_t dat)
{
    for (int t = 0; t < 8; t++)
    {
        IIC_SDA(((dat & 0x80) >> 7));
        IIC_DELAY();
        IIC_SCL(1);
        IIC_DELAY();
        IIC_SCL(0);
        dat <<= 1;
    }
    IIC_SDA(1);
		
	return true;
}

uint8_t SoftWareIIC::ReceiveByte(bool ack)
{
    uint8_t dat = 0;
    
    for (int i = 0; i < 8; i++)
    {
        dat <<= 1;
        IIC_SCL(1);
        IIC_DELAY();
        
        if (IIC_READ_SDA())
        {
            dat++;
        }
        
        IIC_SCL(0);
        IIC_DELAY();
    }
    
    if (ack == false)
    {
        NAckSignal();
    }
    else
    {
        AckSignal();
    }

    return dat;
}
